/*-------------------------------------------------------------------------
 *
 * foreigncmds.c
 *	  外部数据包装器/服务器创建/操作命令
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 *
 *
 * IDENTIFICATION
 *	  src/backend/commands/foreigncmds.c
 *
 *-------------------------------------------------------------------------
 */
#include "postgres.h"

#include "access/htup_details.h"
#include "access/reloptions.h"
#include "access/table.h"
#include "access/xact.h"
#include "catalog/catalog.h"
#include "catalog/dependency.h"
#include "catalog/indexing.h"
#include "catalog/objectaccess.h"
#include "catalog/pg_foreign_data_wrapper.h"
#include "catalog/pg_foreign_server.h"
#include "catalog/pg_foreign_table.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_type.h"
#include "catalog/pg_user_mapping.h"
#include "commands/defrem.h"
#include "foreign/fdwapi.h"
#include "foreign/foreign.h"
#include "miscadmin.h"
#include "parser/parse_func.h"
#include "tcop/utility.h"
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/lsyscache.h"
#include "utils/rel.h"
#include "utils/syscache.h"


typedef struct
{
	char	   *tablename;
	char	   *cmd;
} import_error_callback_arg;

/* 内部函数 */
static void fc_import_error_callback(void *fc_arg);


/*
 * 将DefElem列表转换为pg_foreign_data_wrapper、pg_foreign_server、pg_user_mapping和pg_foreign_table中使用的文本数组格式。
 *
 * 返回形式为Datum的数组，如果列表为空则返回PointerGetDatum(NULL)。
 *
 * 注意：该数组通常在没有进一步处理的情况下存储到数据库中，因此任何验证应在此转换之前完成。
 */
static Datum fc_optionListToArray(List *fc_options)
{
	ArrayBuildState *fc_astate = NULL;
	ListCell   *fc_cell;

	foreach(fc_cell, fc_options)
	{
		DefElem    *fc_def = lfirst(fc_cell);
		const char *fc_value;
		Size		fc_len;
		text	   *fc_t;

		fc_value = defGetString(fc_def);
		fc_len = VARHDRSZ + strlen(fc_def->defname) + 1 + strlen(fc_value);
		fc_t = palloc(fc_len + 1);
		SET_VARSIZE(fc_t, fc_len);
		sprintf(VARDATA(fc_t), "%s=%s", fc_def->defname, fc_value);

		fc_astate = accumArrayResult(fc_astate, PointerGetDatum(fc_t),
								  false, TEXTOID,
								  CurrentMemoryContext);
	}

	if (fc_astate)
		return makeArrayResult(fc_astate, CurrentMemoryContext);

	return PointerGetDatum(NULL);
}


/*
 * 将DefElem列表转换为文本数组格式。这在实质上与optionListToArray()相同，只是我们识别用于修改现有选项列表的SET/ADD/DROP操作，该列表以Datum形式传入，作为oldOptions。而且，如果fdwvalidator不是InvalidOid，它则指定一个用于结果的验证函数。
 *
 * 返回形式为Datum的数组，如果列表为空则返回PointerGetDatum(NULL)。
 *
 * 该功能用于FOREIGN DATA WRAPPER/SERVER/USER MAPPING/FOREIGN TABLE的CREATE/ALTER。
 */
Datum transformGenericOptions(Oid fc_catalogId,
						Datum fc_oldOptions,
						List *fc_options,
						Oid fc_fdwvalidator)
{
	List	   *fc_resultOptions = untransformRelOptions(fc_oldOptions);
	ListCell   *fc_optcell;
	Datum		fc_result;

	foreach(fc_optcell, fc_options)
	{
		DefElem    *fc_od = lfirst(fc_optcell);
		ListCell   *fc_cell;

		/*
		 * 在resultOptions中查找元素。我们在所有情况下都需要这个进行验证。
		 */
		foreach(fc_cell, fc_resultOptions)
		{
			DefElem    *fc_def = lfirst(fc_cell);

			if (strcmp(fc_def->defname, fc_od->defname) == 0)
				break;
		}

		/*
		 * 可以对同一个选项执行多个SET/DROP操作。标准允许这样做，只要要添加的选项是唯一的。注意，未指定的操作视为ADD。
		 */
		switch (fc_od->defaction)
		{
			case DEFELEM_DROP:
				if (!fc_cell)
					ereport(ERROR,
							(errcode(ERRCODE_UNDEFINED_OBJECT),
							 errmsg("option \"%s\" not found",
									fc_od->defname)));
				fc_resultOptions = list_delete_cell(fc_resultOptions, fc_cell);
				break;

			case DEFELEM_SET:
				if (!fc_cell)
					ereport(ERROR,
							(errcode(ERRCODE_UNDEFINED_OBJECT),
							 errmsg("option \"%s\" not found",
									fc_od->defname)));
				lfirst(fc_cell) = fc_od;
				break;

			case DEFELEM_ADD:
			case DEFELEM_UNSPEC:
				if (fc_cell)
					ereport(ERROR,
							(errcode(ERRCODE_DUPLICATE_OBJECT),
							 errmsg("option \"%s\" provided more than once",
									fc_od->defname)));
				fc_resultOptions = lappend(fc_resultOptions, fc_od);
				break;

			default:
				elog(ERROR, "unrecognized action %d on option \"%s\"",
					 (int) fc_od->defaction, fc_od->defname);
				break;
		}
	}

	fc_result = fc_optionListToArray(fc_resultOptions);

	if (OidIsValid(fc_fdwvalidator))
	{
		Datum		fc_valarg = fc_result;

		/*
		 * 将空选项列表作为空数组传递，以便验证器不必声明为非严格来处理这种情况。
		 */
		if (DatumGetPointer(fc_valarg) == NULL)
			fc_valarg = PointerGetDatum(construct_empty_array(TEXTOID));
		OidFunctionCall2(fc_fdwvalidator, fc_valarg, ObjectIdGetDatum(fc_catalogId));
	}

	return fc_result;
}


/*
 * 更改数据包装器的所有者的内部工作程序。
 *
 * 仅允许超级用户进行此操作；新所有者也必须是超级用户。
 */
static void fc_AlterForeignDataWrapperOwner_internal(Relation fc_rel, HeapTuple fc_tup, Oid fc_newOwnerId)
{
	Form_pg_foreign_data_wrapper fc_form;
	Datum		fc_repl_val[Natts_pg_foreign_data_wrapper];
	bool		fc_repl_null[Natts_pg_foreign_data_wrapper];
	bool		fc_repl_repl[Natts_pg_foreign_data_wrapper];
	Acl		   *fc_newAcl;
	Datum		fc_aclDatum;
	bool		fc_isNull;

	fc_form = (Form_pg_foreign_data_wrapper) GETSTRUCT(fc_tup);

	/* 必须是超级用户才能更改FDW所有者 */
	if (!superuser())
		ereport(ERROR,
				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
				 errmsg("permission denied to change owner of foreign-data wrapper \"%s\"",
						NameStr(fc_form->fdwname)),
				 errhint("Must be superuser to change owner of a foreign-data wrapper.")));

	/* 新所有者也必须是超级用户 */
	if (!superuser_arg(fc_newOwnerId))
		ereport(ERROR,
				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
				 errmsg("permission denied to change owner of foreign-data wrapper \"%s\"",
						NameStr(fc_form->fdwname)),
				 errhint("The owner of a foreign-data wrapper must be a superuser.")));

	if (fc_form->fdwowner != fc_newOwnerId)
	{
		memset(fc_repl_null, false, sizeof(fc_repl_null));
		memset(fc_repl_repl, false, sizeof(fc_repl_repl));

		fc_repl_repl[Anum_pg_foreign_data_wrapper_fdwowner - 1] = true;
		fc_repl_val[Anum_pg_foreign_data_wrapper_fdwowner - 1] = ObjectIdGetDatum(fc_newOwnerId);

		fc_aclDatum = heap_getattr(fc_tup,
								Anum_pg_foreign_data_wrapper_fdwacl,
								RelationGetDescr(fc_rel),
								&fc_isNull);
		/* 空ACL不需要更改 */
		if (!fc_isNull)
		{
			fc_newAcl = aclnewowner(DatumGetAclP(fc_aclDatum),
								 fc_form->fdwowner, fc_newOwnerId);
			fc_repl_repl[Anum_pg_foreign_data_wrapper_fdwacl - 1] = true;
			fc_repl_val[Anum_pg_foreign_data_wrapper_fdwacl - 1] = PointerGetDatum(fc_newAcl);
		}

		fc_tup = heap_modify_tuple(fc_tup, RelationGetDescr(fc_rel), fc_repl_val, fc_repl_null,
								fc_repl_repl);

		CatalogTupleUpdate(fc_rel, &fc_tup->t_self, fc_tup);

		/* 更新所有者依赖关系引用 */
		changeDependencyOnOwner(ForeignDataWrapperRelationId,
								fc_form->oid,
								fc_newOwnerId);
	}

	InvokeObjectPostAlterHook(ForeignDataWrapperRelationId,
							  fc_form->oid, 0);
}

/*
 * 更改外部数据包装器所有者 -- 按名称
 *
 * 注意上述"_internal"函数中的限制。
 */
ObjectAddress AlterForeignDataWrapperOwner(const char *fc_name, Oid fc_newOwnerId)
{
	Oid			fc_fdwId;
	HeapTuple	fc_tup;
	Relation	fc_rel;
	ObjectAddress fc_address;
	Form_pg_foreign_data_wrapper fc_form;


	fc_rel = table_open(ForeignDataWrapperRelationId, RowExclusiveLock);

	fc_tup = SearchSysCacheCopy1(FOREIGNDATAWRAPPERNAME, CStringGetDatum(fc_name));

	if (!HeapTupleIsValid(fc_tup))
		ereport(ERROR,
				(errcode(ERRCODE_UNDEFINED_OBJECT),
				 errmsg("foreign-data wrapper \"%s\" does not exist", fc_name)));

	fc_form = (Form_pg_foreign_data_wrapper) GETSTRUCT(fc_tup);
	fc_fdwId = fc_form->oid;

	fc_AlterForeignDataWrapperOwner_internal(fc_rel, fc_tup, fc_newOwnerId);

	ObjectAddressSet(fc_address, ForeignDataWrapperRelationId, fc_fdwId);

	heap_freetuple(fc_tup);

	table_close(fc_rel, RowExclusiveLock);

	return fc_address;
}

/*
 * 更改外部数据包装器所有者 -- 按OID
 *
 * 注意上述"_internal"函数中的限制。
 */
void AlterForeignDataWrapperOwner_oid(Oid fc_fwdId, Oid fc_newOwnerId)
{
	HeapTuple	fc_tup;
	Relation	fc_rel;

	fc_rel = table_open(ForeignDataWrapperRelationId, RowExclusiveLock);

	fc_tup = SearchSysCacheCopy1(FOREIGNDATAWRAPPEROID, ObjectIdGetDatum(fc_fwdId));

	if (!HeapTupleIsValid(fc_tup))
		ereport(ERROR,
				(errcode(ERRCODE_UNDEFINED_OBJECT),
				 errmsg("foreign-data wrapper with OID %u does not exist", fc_fwdId)));

	fc_AlterForeignDataWrapperOwner_internal(fc_rel, fc_tup, fc_newOwnerId);

	heap_freetuple(fc_tup);

	table_close(fc_rel, RowExclusiveLock);
}

/*
 * 更改外部服务器所有者的内部工作程序
 */
static void fc_AlterForeignServerOwner_internal(Relation fc_rel, HeapTuple fc_tup, Oid fc_newOwnerId)
{
	Form_pg_foreign_server fc_form;
	Datum		fc_repl_val[Natts_pg_foreign_server];
	bool		fc_repl_null[Natts_pg_foreign_server];
	bool		fc_repl_repl[Natts_pg_foreign_server];
	Acl		   *fc_newAcl;
	Datum		fc_aclDatum;
	bool		fc_isNull;

	fc_form = (Form_pg_foreign_server) GETSTRUCT(fc_tup);

	if (fc_form->srvowner != fc_newOwnerId)
	{
		/* 超级用户可以始终执行此操作 */
		if (!superuser())
		{
			Oid			fc_srvId;
			AclResult	fc_aclresult;

			fc_srvId = fc_form->oid;

			/* 必须是拥有者 */
			if (!pg_foreign_server_ownercheck(fc_srvId, GetUserId()))
				aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_FOREIGN_SERVER,
							   NameStr(fc_form->srvname));

			/* 必须能够成为新所有者 */
			check_is_member_of_role(GetUserId(), fc_newOwnerId);

			/* 新所有者必须对外部数据包装器具有USAGE权限 */
			fc_aclresult = pg_foreign_data_wrapper_aclcheck(fc_form->srvfdw, fc_newOwnerId, ACL_USAGE);
			if (fc_aclresult != ACLCHECK_OK)
			{
				ForeignDataWrapper *fc_fdw = GetForeignDataWrapper(fc_form->srvfdw);

				aclcheck_error(fc_aclresult, OBJECT_FDW, fc_fdw->fdwname);
			}
		}

		memset(fc_repl_null, false, sizeof(fc_repl_null));
		memset(fc_repl_repl, false, sizeof(fc_repl_repl));

		fc_repl_repl[Anum_pg_foreign_server_srvowner - 1] = true;
		fc_repl_val[Anum_pg_foreign_server_srvowner - 1] = ObjectIdGetDatum(fc_newOwnerId);

		fc_aclDatum = heap_getattr(fc_tup,
								Anum_pg_foreign_server_srvacl,
								RelationGetDescr(fc_rel),
								&fc_isNull);
		/* 空ACL不需要更改 */
		if (!fc_isNull)
		{
			fc_newAcl = aclnewowner(DatumGetAclP(fc_aclDatum),
								 fc_form->srvowner, fc_newOwnerId);
			fc_repl_repl[Anum_pg_foreign_server_srvacl - 1] = true;
			fc_repl_val[Anum_pg_foreign_server_srvacl - 1] = PointerGetDatum(fc_newAcl);
		}

		fc_tup = heap_modify_tuple(fc_tup, RelationGetDescr(fc_rel), fc_repl_val, fc_repl_null,
								fc_repl_repl);

		CatalogTupleUpdate(fc_rel, &fc_tup->t_self, fc_tup);

		/* 更新所有者依赖关系引用 */
		changeDependencyOnOwner(ForeignServerRelationId, fc_form->oid,
								fc_newOwnerId);
	}

	InvokeObjectPostAlterHook(ForeignServerRelationId,
							  fc_form->oid, 0);
}

/*
 * 更改外部服务器所有者 -- 按名称
 */
ObjectAddress AlterForeignServerOwner(const char *fc_name, Oid fc_newOwnerId)
{
	Oid			fc_servOid;
	HeapTuple	fc_tup;
	Relation	fc_rel;
	ObjectAddress fc_address;
	Form_pg_foreign_server fc_form;

	fc_rel = table_open(ForeignServerRelationId, RowExclusiveLock);

	fc_tup = SearchSysCacheCopy1(FOREIGNSERVERNAME, CStringGetDatum(fc_name));

	if (!HeapTupleIsValid(fc_tup))
		ereport(ERROR,
				(errcode(ERRCODE_UNDEFINED_OBJECT),
				 errmsg("server \"%s\" does not exist", fc_name)));

	fc_form = (Form_pg_foreign_server) GETSTRUCT(fc_tup);
	fc_servOid = fc_form->oid;

	fc_AlterForeignServerOwner_internal(fc_rel, fc_tup, fc_newOwnerId);

	ObjectAddressSet(fc_address, ForeignServerRelationId, fc_servOid);

	heap_freetuple(fc_tup);

	table_close(fc_rel, RowExclusiveLock);

	return fc_address;
}

/*
 * 更改外部服务器所有者 -- 按OID
 */
void AlterForeignServerOwner_oid(Oid fc_srvId, Oid fc_newOwnerId)
{
	HeapTuple	fc_tup;
	Relation	fc_rel;

	fc_rel = table_open(ForeignServerRelationId, RowExclusiveLock);

	fc_tup = SearchSysCacheCopy1(FOREIGNSERVEROID, ObjectIdGetDatum(fc_srvId));

	if (!HeapTupleIsValid(fc_tup))
		ereport(ERROR,
				(errcode(ERRCODE_UNDEFINED_OBJECT),
				 errmsg("foreign server with OID %u does not exist", fc_srvId)));

	fc_AlterForeignServerOwner_internal(fc_rel, fc_tup, fc_newOwnerId);

	heap_freetuple(fc_tup);

	table_close(fc_rel, RowExclusiveLock);
}

/*
 * 将从解析器传入的处理函数名称转换为Oid。
 */
static Oid fc_lookup_fdw_handler_func(DefElem *fc_handler)
{
	Oid			fc_handlerOid;

	if (fc_handler == NULL || fc_handler->arg == NULL)
		return InvalidOid;

	/* 处理程序没有参数 */
	fc_handlerOid = LookupFuncName((List *) fc_handler->arg, 0, NULL, false);

	/* 检查处理程序具有正确的返回类型 */
	if (get_func_rettype(fc_handlerOid) != FDW_HANDLEROID)
		ereport(ERROR,
				(errcode(ERRCODE_WRONG_OBJECT_TYPE),
				 errmsg("function %s must return type %s",
						NameListToString((List *) fc_handler->arg), "fdw_handler")));

	return fc_handlerOid;
}

/*
 * 将从解析器传入的验证函数名称转换为Oid。
 */
static Oid fc_lookup_fdw_validator_func(DefElem *fc_validator)
{
	Oid			fc_funcargtypes[2];

	if (fc_validator == NULL || fc_validator->arg == NULL)
		return InvalidOid;

	/* 验证器接受text[], oid */
	fc_funcargtypes[0] = TEXTARRAYOID;
	fc_funcargtypes[1] = OIDOID;

	return LookupFuncName((List *) fc_validator->arg, 2, fc_funcargtypes, false);
	/* 验证器的返回值被忽略，因此我们不检查类型 */
}

/*
 * 处理CREATE/ALTER FDW的函数选项
 */
static void fc_parse_func_options(ParseState *fc_pstate, List *fc_func_options,
				   bool *fc_handler_given, Oid *fc_fdwhandler,
				   bool *fc_validator_given, Oid *fc_fdwvalidator)
{
	ListCell   *fc_cell;

	*fc_handler_given = false;
	*fc_validator_given = false;
	/* 如果未给出则返回InvalidOid */
	*fc_fdwhandler = InvalidOid;
	*fc_fdwvalidator = InvalidOid;

	foreach(fc_cell, fc_func_options)
	{
		DefElem    *fc_def = (DefElem *) lfirst(fc_cell);

		if (strcmp(fc_def->defname, "handler") == 0)
		{
			if (*fc_handler_given)
				errorConflictingDefElem(fc_def, fc_pstate);
			*fc_handler_given = true;
			*fc_fdwhandler = fc_lookup_fdw_handler_func(fc_def);
		}
		else if (strcmp(fc_def->defname, "validator") == 0)
		{
			if (*fc_validator_given)
				errorConflictingDefElem(fc_def, fc_pstate);
			*fc_validator_given = true;
			*fc_fdwvalidator = fc_lookup_fdw_validator_func(fc_def);
		}
		else
			elog(ERROR, "option \"%s\" not recognized",
				 fc_def->defname);
	}
}

/*
 * 创建外部数据包装器
 */
ObjectAddress CreateForeignDataWrapper(ParseState *fc_pstate, CreateFdwStmt *fc_stmt)
{
	Relation	fc_rel;
	Datum		fc_values[Natts_pg_foreign_data_wrapper];
	bool		fc_nulls[Natts_pg_foreign_data_wrapper];
	HeapTuple	fc_tuple;
	Oid			fc_fdwId;
	bool		fc_handler_given;
	bool		fc_validator_given;
	Oid			fc_fdwhandler;
	Oid			fc_fdwvalidator;
	Datum		fc_fdwoptions;
	Oid			fc_ownerId;
	ObjectAddress fc_myself;
	ObjectAddress fc_referenced;

	fc_rel = table_open(ForeignDataWrapperRelationId, RowExclusiveLock);

	/* 必须是超级用户 */
	if (!superuser())
		ereport(ERROR,
				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
				 errmsg("permission denied to create foreign-data wrapper \"%s\"",
						fc_stmt->fdwname),
				 errhint("Must be superuser to create a foreign-data wrapper.")));

	/* 目前在创建时无法指定所有者。使用有效用户ID。 */
	fc_ownerId = GetUserId();

	/*
	 * 检查是否没有其他同名的外部数据包装器。
	 */
	if (GetForeignDataWrapperByName(fc_stmt->fdwname, true) != NULL)
		ereport(ERROR,
				(errcode(ERRCODE_DUPLICATE_OBJECT),
				 errmsg("foreign-data wrapper \"%s\" already exists",
						fc_stmt->fdwname)));

	/*
	 * 插入元组到pg_foreign_data_wrapper。
	 */
	memset(fc_values, 0, sizeof(fc_values));
	memset(fc_nulls, false, sizeof(fc_nulls));

	fc_fdwId = GetNewOidWithIndex(fc_rel, ForeignDataWrapperOidIndexId,
							   Anum_pg_foreign_data_wrapper_oid);
	fc_values[Anum_pg_foreign_data_wrapper_oid - 1] = ObjectIdGetDatum(fc_fdwId);
	fc_values[Anum_pg_foreign_data_wrapper_fdwname - 1] =
		DirectFunctionCall1(namein, CStringGetDatum(fc_stmt->fdwname));
	fc_values[Anum_pg_foreign_data_wrapper_fdwowner - 1] = ObjectIdGetDatum(fc_ownerId);

	/* 查找处理程序和验证函数（如果提供） */
	fc_parse_func_options(fc_pstate, fc_stmt->func_options,
					   &fc_handler_given, &fc_fdwhandler,
					   &fc_validator_given, &fc_fdwvalidator);

	fc_values[Anum_pg_foreign_data_wrapper_fdwhandler - 1] = ObjectIdGetDatum(fc_fdwhandler);
	fc_values[Anum_pg_foreign_data_wrapper_fdwvalidator - 1] = ObjectIdGetDatum(fc_fdwvalidator);

	fc_nulls[Anum_pg_foreign_data_wrapper_fdwacl - 1] = true;

	fc_fdwoptions = transformGenericOptions(ForeignDataWrapperRelationId,
										 PointerGetDatum(NULL),
										 fc_stmt->options,
										 fc_fdwvalidator);

	if (PointerIsValid(DatumGetPointer(fc_fdwoptions)))
		fc_values[Anum_pg_foreign_data_wrapper_fdwoptions - 1] = fc_fdwoptions;
	else
		fc_nulls[Anum_pg_foreign_data_wrapper_fdwoptions - 1] = true;

	fc_tuple = heap_form_tuple(fc_rel->rd_att, fc_values, fc_nulls);

	CatalogTupleInsert(fc_rel, fc_tuple);

	heap_freetuple(fc_tuple);

	/* 记录依赖关系 */
	fc_myself.classId = ForeignDataWrapperRelationId;
	fc_myself.objectId = fc_fdwId;
	fc_myself.objectSubId = 0;

	if (OidIsValid(fc_fdwhandler))
	{
		fc_referenced.classId = ProcedureRelationId;
		fc_referenced.objectId = fc_fdwhandler;
		fc_referenced.objectSubId = 0;
		recordDependencyOn(&fc_myself, &fc_referenced, DEPENDENCY_NORMAL);
	}

	if (OidIsValid(fc_fdwvalidator))
	{
		fc_referenced.classId = ProcedureRelationId;
		fc_referenced.objectId = fc_fdwvalidator;
		fc_referenced.objectSubId = 0;
		recordDependencyOn(&fc_myself, &fc_referenced, DEPENDENCY_NORMAL);
	}

	recordDependencyOnOwner(ForeignDataWrapperRelationId, fc_fdwId, fc_ownerId);

	/* 对扩展的依赖 */
	recordDependencyOnCurrentExtension(&fc_myself, false);

	/* 新外部数据包装器的创建后钩子 */
	InvokeObjectPostCreateHook(ForeignDataWrapperRelationId, fc_fdwId, 0);

	table_close(fc_rel, RowExclusiveLock);

	return fc_myself;
}


/*
 * 修改外部数据包装器
 */
ObjectAddress AlterForeignDataWrapper(ParseState *fc_pstate, AlterFdwStmt *fc_stmt)
{
	Relation	fc_rel;
	HeapTuple	fc_tp;
	Form_pg_foreign_data_wrapper fc_fdwForm;
	Datum		fc_repl_val[Natts_pg_foreign_data_wrapper];
	bool		fc_repl_null[Natts_pg_foreign_data_wrapper];
	bool		fc_repl_repl[Natts_pg_foreign_data_wrapper];
	Oid			fc_fdwId;
	bool		fc_isnull;
	Datum		fc_datum;
	bool		fc_handler_given;
	bool		fc_validator_given;
	Oid			fc_fdwhandler;
	Oid			fc_fdwvalidator;
	ObjectAddress fc_myself;

	fc_rel = table_open(ForeignDataWrapperRelationId, RowExclusiveLock);

	/* 必须是超级用户 */
	if (!superuser())
		ereport(ERROR,
				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
				 errmsg("permission denied to alter foreign-data wrapper \"%s\"",
						fc_stmt->fdwname),
				 errhint("Must be superuser to alter a foreign-data wrapper.")));

	fc_tp = SearchSysCacheCopy1(FOREIGNDATAWRAPPERNAME,
							 CStringGetDatum(fc_stmt->fdwname));

	if (!HeapTupleIsValid(fc_tp))
		ereport(ERROR,
				(errcode(ERRCODE_UNDEFINED_OBJECT),
				 errmsg("foreign-data wrapper \"%s\" does not exist", fc_stmt->fdwname)));

	fc_fdwForm = (Form_pg_foreign_data_wrapper) GETSTRUCT(fc_tp);
	fc_fdwId = fc_fdwForm->oid;

	memset(fc_repl_val, 0, sizeof(fc_repl_val));
	memset(fc_repl_null, false, sizeof(fc_repl_null));
	memset(fc_repl_repl, false, sizeof(fc_repl_repl));

	fc_parse_func_options(fc_pstate, fc_stmt->func_options,
					   &fc_handler_given, &fc_fdwhandler,
					   &fc_validator_given, &fc_fdwvalidator);

	if (fc_handler_given)
	{
		fc_repl_val[Anum_pg_foreign_data_wrapper_fdwhandler - 1] = ObjectIdGetDatum(fc_fdwhandler);
		fc_repl_repl[Anum_pg_foreign_data_wrapper_fdwhandler - 1] = true;

		/*
		 * 访问外部表的行为可能会因新处理程序而变化。
		 * 对此进行警告。
		 */
		ereport(WARNING,
				(errmsg("changing the foreign-data wrapper handler can change behavior of existing foreign tables")));
	}

	if (fc_validator_given)
	{
		fc_repl_val[Anum_pg_foreign_data_wrapper_fdwvalidator - 1] = ObjectIdGetDatum(fc_fdwvalidator);
		fc_repl_repl[Anum_pg_foreign_data_wrapper_fdwvalidator - 1] = true;

		/*
		 * 现有的FDW或依赖的SERVER、
		 * USER MAPPING或FOREIGN TABLE对象的选项可能不再有效
		 * 根据新的验证器。对此进行警告。
		 */
		if (OidIsValid(fc_fdwvalidator))
			ereport(WARNING,
					(errmsg("changing the foreign-data wrapper validator can cause "
							"the options for dependent objects to become invalid")));
	}
	else
	{
		/*
		 * 验证器没有改变，但我们需要它来验证选项。
		 */
		fc_fdwvalidator = fc_fdwForm->fdwvalidator;
	}

	/*
	 * 如果指定了选项，则验证并更新。
	 */
	if (fc_stmt->options)
	{
		/* 提取当前选项 */
		fc_datum = SysCacheGetAttr(FOREIGNDATAWRAPPEROID,
								fc_tp,
								Anum_pg_foreign_data_wrapper_fdwoptions,
								&fc_isnull);
		if (fc_isnull)
			fc_datum = PointerGetDatum(NULL);

		/* 转换选项 */
		fc_datum = transformGenericOptions(ForeignDataWrapperRelationId,
										fc_datum,
										fc_stmt->options,
										fc_fdwvalidator);

		if (PointerIsValid(DatumGetPointer(fc_datum)))
			fc_repl_val[Anum_pg_foreign_data_wrapper_fdwoptions - 1] = fc_datum;
		else
			fc_repl_null[Anum_pg_foreign_data_wrapper_fdwoptions - 1] = true;

		fc_repl_repl[Anum_pg_foreign_data_wrapper_fdwoptions - 1] = true;
	}

	/* 一切看起来都很好 - 更新元组 */
	fc_tp = heap_modify_tuple(fc_tp, RelationGetDescr(fc_rel),
						   fc_repl_val, fc_repl_null, fc_repl_repl);

	CatalogTupleUpdate(fc_rel, &fc_tp->t_self, fc_tp);

	heap_freetuple(fc_tp);

	ObjectAddressSet(fc_myself, ForeignDataWrapperRelationId, fc_fdwId);

	/* 如果我们更改了函数依赖关系，更新函数依赖关系 */
	if (fc_handler_given || fc_validator_given)
	{
		ObjectAddress fc_referenced;

		/*
		 * 刷新此FDW在函数上的所有现有依赖记录；我们
		 * 假设除了我们正在修复的之外，没有其他记录。
		 */
		deleteDependencyRecordsForClass(ForeignDataWrapperRelationId,
										fc_fdwId,
										ProcedureRelationId,
										DEPENDENCY_NORMAL);

		/* 并构建新的记录。 */

		if (OidIsValid(fc_fdwhandler))
		{
			fc_referenced.classId = ProcedureRelationId;
			fc_referenced.objectId = fc_fdwhandler;
			fc_referenced.objectSubId = 0;
			recordDependencyOn(&fc_myself, &fc_referenced, DEPENDENCY_NORMAL);
		}

		if (OidIsValid(fc_fdwvalidator))
		{
			fc_referenced.classId = ProcedureRelationId;
			fc_referenced.objectId = fc_fdwvalidator;
			fc_referenced.objectSubId = 0;
			recordDependencyOn(&fc_myself, &fc_referenced, DEPENDENCY_NORMAL);
		}
	}

	InvokeObjectPostAlterHook(ForeignDataWrapperRelationId, fc_fdwId, 0);

	table_close(fc_rel, RowExclusiveLock);

	return fc_myself;
}


/*
 * 创建外部服务器
 */
ObjectAddress CreateForeignServer(CreateForeignServerStmt *fc_stmt)
{
	Relation	fc_rel;
	Datum		fc_srvoptions;
	Datum		fc_values[Natts_pg_foreign_server];
	bool		fc_nulls[Natts_pg_foreign_server];
	HeapTuple	fc_tuple;
	Oid			fc_srvId;
	Oid			fc_ownerId;
	AclResult	fc_aclresult;
	ObjectAddress fc_myself;
	ObjectAddress fc_referenced;
	ForeignDataWrapper *fc_fdw;

	fc_rel = table_open(ForeignServerRelationId, RowExclusiveLock);

	/* 目前在创建时无法指定所有者。使用有效用户ID。 */
	fc_ownerId = GetUserId();

	/*
	 * 检查是否没有其他同名的外部服务器。如果存在
	 * 一个，如果指定了IF NOT EXISTS，则不执行任何操作。
	 */
	fc_srvId = get_foreign_server_oid(fc_stmt->servername, true);
	if (OidIsValid(fc_srvId))
	{
		if (fc_stmt->if_not_exists)
		{
			/*
			 * 如果我们处于扩展脚本中，必须确保预先存在的
			 * 对象是扩展的成员，以避免安全风险。
			 */
			ObjectAddressSet(fc_myself, ForeignServerRelationId, fc_srvId);
			checkMembershipInCurrentExtension(&fc_myself);

			/* 可以跳过 */
			ereport(NOTICE,
					(errcode(ERRCODE_DUPLICATE_OBJECT),
					 errmsg("server \"%s\" already exists, skipping",
							fc_stmt->servername)));
			table_close(fc_rel, RowExclusiveLock);
			return InvalidObjectAddress;
		}
		else
			ereport(ERROR,
					(errcode(ERRCODE_DUPLICATE_OBJECT),
					 errmsg("server \"%s\" already exists",
							fc_stmt->servername)));
	}

	/*
	 * 检查FDW是否存在，并且我们对此拥有使用权限。同时获取
	 * 实际的FDW进行选项验证等。
	 */
	fc_fdw = GetForeignDataWrapperByName(fc_stmt->fdwname, false);

	fc_aclresult = pg_foreign_data_wrapper_aclcheck(fc_fdw->fdwid, fc_ownerId, ACL_USAGE);
	if (fc_aclresult != ACLCHECK_OK)
		aclcheck_error(fc_aclresult, OBJECT_FDW, fc_fdw->fdwname);

	/*
	 * 插入元组到pg_foreign_server。
	 */
	memset(fc_values, 0, sizeof(fc_values));
	memset(fc_nulls, false, sizeof(fc_nulls));

	fc_srvId = GetNewOidWithIndex(fc_rel, ForeignServerOidIndexId,
							   Anum_pg_foreign_server_oid);
	fc_values[Anum_pg_foreign_server_oid - 1] = ObjectIdGetDatum(fc_srvId);
	fc_values[Anum_pg_foreign_server_srvname - 1] =
		DirectFunctionCall1(namein, CStringGetDatum(fc_stmt->servername));
	fc_values[Anum_pg_foreign_server_srvowner - 1] = ObjectIdGetDatum(fc_ownerId);
	fc_values[Anum_pg_foreign_server_srvfdw - 1] = ObjectIdGetDatum(fc_fdw->fdwid);

	/* 如果提供，添加服务器类型 */
	if (fc_stmt->servertype)
		fc_values[Anum_pg_foreign_server_srvtype - 1] =
			CStringGetTextDatum(fc_stmt->servertype);
	else
		fc_nulls[Anum_pg_foreign_server_srvtype - 1] = true;

	/* 如果提供，添加服务器版本 */
	if (fc_stmt->version)
		fc_values[Anum_pg_foreign_server_srvversion - 1] =
			CStringGetTextDatum(fc_stmt->version);
	else
		fc_nulls[Anum_pg_foreign_server_srvversion - 1] = true;

	/* 以空的acl开始 */
	fc_nulls[Anum_pg_foreign_server_srvacl - 1] = true;

	/* 添加服务器选项 */
	fc_srvoptions = transformGenericOptions(ForeignServerRelationId,
										 PointerGetDatum(NULL),
										 fc_stmt->options,
										 fc_fdw->fdwvalidator);

	if (PointerIsValid(DatumGetPointer(fc_srvoptions)))
		fc_values[Anum_pg_foreign_server_srvoptions - 1] = fc_srvoptions;
	else
		fc_nulls[Anum_pg_foreign_server_srvoptions - 1] = true;

	fc_tuple = heap_form_tuple(fc_rel->rd_att, fc_values, fc_nulls);

	CatalogTupleInsert(fc_rel, fc_tuple);

	heap_freetuple(fc_tuple);

	/* 记录依赖关系 */
	fc_myself.classId = ForeignServerRelationId;
	fc_myself.objectId = fc_srvId;
	fc_myself.objectSubId = 0;

	fc_referenced.classId = ForeignDataWrapperRelationId;
	fc_referenced.objectId = fc_fdw->fdwid;
	fc_referenced.objectSubId = 0;
	recordDependencyOn(&fc_myself, &fc_referenced, DEPENDENCY_NORMAL);

	recordDependencyOnOwner(ForeignServerRelationId, fc_srvId, fc_ownerId);

	/* 对扩展的依赖 */
	recordDependencyOnCurrentExtension(&fc_myself, false);

	/* 新外部服务器的创建后钩子 */
	InvokeObjectPostCreateHook(ForeignServerRelationId, fc_srvId, 0);

	table_close(fc_rel, RowExclusiveLock);

	return fc_myself;
}


/*
 * 修改外部服务器
 */
ObjectAddress AlterForeignServer(AlterForeignServerStmt *fc_stmt)
{
	Relation	fc_rel;
	HeapTuple	fc_tp;
	Datum		fc_repl_val[Natts_pg_foreign_server];
	bool		fc_repl_null[Natts_pg_foreign_server];
	bool		fc_repl_repl[Natts_pg_foreign_server];
	Oid			fc_srvId;
	Form_pg_foreign_server fc_srvForm;
	ObjectAddress fc_address;

	fc_rel = table_open(ForeignServerRelationId, RowExclusiveLock);

	fc_tp = SearchSysCacheCopy1(FOREIGNSERVERNAME,
							 CStringGetDatum(fc_stmt->servername));

	if (!HeapTupleIsValid(fc_tp))
		ereport(ERROR,
				(errcode(ERRCODE_UNDEFINED_OBJECT),
				 errmsg("server \"%s\" does not exist", fc_stmt->servername)));

	fc_srvForm = (Form_pg_foreign_server) GETSTRUCT(fc_tp);
	fc_srvId = fc_srvForm->oid;

	/*
	 * 只有所有者或超级用户可以修改SERVER。
	 */
	if (!pg_foreign_server_ownercheck(fc_srvId, GetUserId()))
		aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_FOREIGN_SERVER,
					   fc_stmt->servername);

	memset(fc_repl_val, 0, sizeof(fc_repl_val));
	memset(fc_repl_null, false, sizeof(fc_repl_null));
	memset(fc_repl_repl, false, sizeof(fc_repl_repl));

	if (fc_stmt->has_version)
	{
		/*
		 * 更改服务器版本字符串。
		 */
		if (fc_stmt->version)
			fc_repl_val[Anum_pg_foreign_server_srvversion - 1] =
				CStringGetTextDatum(fc_stmt->version);
		else
			fc_repl_null[Anum_pg_foreign_server_srvversion - 1] = true;

		fc_repl_repl[Anum_pg_foreign_server_srvversion - 1] = true;
	}

	if (fc_stmt->options)
	{
		ForeignDataWrapper *fc_fdw = GetForeignDataWrapper(fc_srvForm->srvfdw);
		Datum		fc_datum;
		bool		fc_isnull;

		/* 提取当前srvoptions */
		fc_datum = SysCacheGetAttr(FOREIGNSERVEROID,
								fc_tp,
								Anum_pg_foreign_server_srvoptions,
								&fc_isnull);
		if (fc_isnull)
			fc_datum = PointerGetDatum(NULL);

		/* 准备选项数组 */
		fc_datum = transformGenericOptions(ForeignServerRelationId,
										fc_datum,
										fc_stmt->options,
										fc_fdw->fdwvalidator);

		if (PointerIsValid(DatumGetPointer(fc_datum)))
			fc_repl_val[Anum_pg_foreign_server_srvoptions - 1] = fc_datum;
		else
			fc_repl_null[Anum_pg_foreign_server_srvoptions - 1] = true;

		fc_repl_repl[Anum_pg_foreign_server_srvoptions - 1] = true;
	}

	/* 一切看起来都很好 - 更新元组 */
	fc_tp = heap_modify_tuple(fc_tp, RelationGetDescr(fc_rel),
						   fc_repl_val, fc_repl_null, fc_repl_repl);

	CatalogTupleUpdate(fc_rel, &fc_tp->t_self, fc_tp);

	InvokeObjectPostAlterHook(ForeignServerRelationId, fc_srvId, 0);

	ObjectAddressSet(fc_address, ForeignServerRelationId, fc_srvId);

	heap_freetuple(fc_tp);

	table_close(fc_rel, RowExclusiveLock);

	return fc_address;
}


/*
 * 检查与用户映射相关的DDL命令的权限的公共例程。
 * 我们允许服务器所有者操作任何映射，允许用户操作他们自己的映射。
 */
static void fc_user_mapping_ddl_aclcheck(Oid fc_umuserid, Oid fc_serverid, const char *fc_servername)
{
	Oid			fc_curuserid = GetUserId();

	if (!pg_foreign_server_ownercheck(fc_serverid, fc_curuserid))
	{
		if (fc_umuserid == fc_curuserid)
		{
			AclResult	fc_aclresult;

			fc_aclresult = pg_foreign_server_aclcheck(fc_serverid, fc_curuserid, ACL_USAGE);
			if (fc_aclresult != ACLCHECK_OK)
				aclcheck_error(fc_aclresult, OBJECT_FOREIGN_SERVER, fc_servername);
		}
		else
			aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_FOREIGN_SERVER,
						   fc_servername);
	}
}


/*
 * 创建用户映射
 */
ObjectAddress CreateUserMapping(CreateUserMappingStmt *fc_stmt)
{
	Relation	fc_rel;
	Datum		fc_useoptions;
	Datum		fc_values[Natts_pg_user_mapping];
	bool		fc_nulls[Natts_pg_user_mapping];
	HeapTuple	fc_tuple;
	Oid			fc_useId;
	Oid			fc_umId;
	ObjectAddress fc_myself;
	ObjectAddress fc_referenced;
	ForeignServer *fc_srv;
	ForeignDataWrapper *fc_fdw;
	RoleSpec   *fc_role = (RoleSpec *) fc_stmt->user;

	fc_rel = table_open(UserMappingRelationId, RowExclusiveLock);

	if (fc_role->roletype == ROLESPEC_PUBLIC)
		fc_useId = ACL_ID_PUBLIC;
	else
		fc_useId = get_rolespec_oid(fc_stmt->user, false);

	/* 检查服务器是否存在。 */
	fc_srv = GetForeignServerByName(fc_stmt->servername, false);

	fc_user_mapping_ddl_aclcheck(fc_useId, fc_srv->serverid, fc_stmt->servername);

	/*
	 * 检查用户映射在服务器内是否唯一。
	 */
	fc_umId = GetSysCacheOid2(USERMAPPINGUSERSERVER, Anum_pg_user_mapping_oid,
						   ObjectIdGetDatum(fc_useId),
						   ObjectIdGetDatum(fc_srv->serverid));

	if (OidIsValid(fc_umId))
	{
		if (fc_stmt->if_not_exists)
		{
			/*
			 * 由于用户映射不是扩展的成员（见下文评论），
			 * 此处无需进行checkMembershipInCurrentExtension检查。
			 */
			ereport(NOTICE,
					(errcode(ERRCODE_DUPLICATE_OBJECT),
					 errmsg("user mapping for \"%s\" already exists for server \"%s\", skipping",
							MappingUserName(fc_useId),
							fc_stmt->servername)));

			table_close(fc_rel, RowExclusiveLock);
			return InvalidObjectAddress;
		}
		else
			ereport(ERROR,
					(errcode(ERRCODE_DUPLICATE_OBJECT),
					 errmsg("user mapping for \"%s\" already exists for server \"%s\"",
							MappingUserName(fc_useId),
							fc_stmt->servername)));
	}

	fc_fdw = GetForeignDataWrapper(fc_srv->fdwid);

	/*
	 * 插入元组到pg_user_mapping。
	 */
	memset(fc_values, 0, sizeof(fc_values));
	memset(fc_nulls, false, sizeof(fc_nulls));

	fc_umId = GetNewOidWithIndex(fc_rel, UserMappingOidIndexId,
							  Anum_pg_user_mapping_oid);
	fc_values[Anum_pg_user_mapping_oid - 1] = ObjectIdGetDatum(fc_umId);
	fc_values[Anum_pg_user_mapping_umuser - 1] = ObjectIdGetDatum(fc_useId);
	fc_values[Anum_pg_user_mapping_umserver - 1] = ObjectIdGetDatum(fc_srv->serverid);

	/* 添加用户选项 */
	fc_useoptions = transformGenericOptions(UserMappingRelationId,
										 PointerGetDatum(NULL),
										 fc_stmt->options,
										 fc_fdw->fdwvalidator);

	if (PointerIsValid(DatumGetPointer(fc_useoptions)))
		fc_values[Anum_pg_user_mapping_umoptions - 1] = fc_useoptions;
	else
		fc_nulls[Anum_pg_user_mapping_umoptions - 1] = true;

	fc_tuple = heap_form_tuple(fc_rel->rd_att, fc_values, fc_nulls);

	CatalogTupleInsert(fc_rel, fc_tuple);

	heap_freetuple(fc_tuple);

	/* 添加对服务器的依赖 */
	fc_myself.classId = UserMappingRelationId;
	fc_myself.objectId = fc_umId;
	fc_myself.objectSubId = 0;

	fc_referenced.classId = ForeignServerRelationId;
	fc_referenced.objectId = fc_srv->serverid;
	fc_referenced.objectSubId = 0;
	recordDependencyOn(&fc_myself, &fc_referenced, DEPENDENCY_NORMAL);

	if (OidIsValid(fc_useId))
	{
		/* 记录映射用户的依赖 */
		recordDependencyOnOwner(UserMappingRelationId, fc_umId, fc_useId);
	}

	/*
	 * 或许将来应该在这里调用 recordDependencyOnCurrentExtension；
	 * 但是由于角色不是扩展的成员，似乎用户映射也不应该是。
	 * 注意，如果我们更改此内容，语法和 pg_dump 也需要扩展。
	 */

	/* 新用户映射的后创建钩子 */
	InvokeObjectPostCreateHook(UserMappingRelationId, fc_umId, 0);

	table_close(fc_rel, RowExclusiveLock);

	return fc_myself;
}


/*
 * 修改用户映射
 */
ObjectAddress AlterUserMapping(AlterUserMappingStmt *fc_stmt)
{
	Relation	fc_rel;
	HeapTuple	fc_tp;
	Datum		fc_repl_val[Natts_pg_user_mapping];
	bool		fc_repl_null[Natts_pg_user_mapping];
	bool		fc_repl_repl[Natts_pg_user_mapping];
	Oid			fc_useId;
	Oid			fc_umId;
	ForeignServer *fc_srv;
	ObjectAddress fc_address;
	RoleSpec   *fc_role = (RoleSpec *) fc_stmt->user;

	fc_rel = table_open(UserMappingRelationId, RowExclusiveLock);

	if (fc_role->roletype == ROLESPEC_PUBLIC)
		fc_useId = ACL_ID_PUBLIC;
	else
		fc_useId = get_rolespec_oid(fc_stmt->user, false);

	fc_srv = GetForeignServerByName(fc_stmt->servername, false);

	fc_umId = GetSysCacheOid2(USERMAPPINGUSERSERVER, Anum_pg_user_mapping_oid,
						   ObjectIdGetDatum(fc_useId),
						   ObjectIdGetDatum(fc_srv->serverid));
	if (!OidIsValid(fc_umId))
		ereport(ERROR,
				(errcode(ERRCODE_UNDEFINED_OBJECT),
				 errmsg("user mapping for \"%s\" does not exist for server \"%s\"",
						MappingUserName(fc_useId), fc_stmt->servername)));

	fc_user_mapping_ddl_aclcheck(fc_useId, fc_srv->serverid, fc_stmt->servername);

	fc_tp = SearchSysCacheCopy1(USERMAPPINGOID, ObjectIdGetDatum(fc_umId));

	if (!HeapTupleIsValid(fc_tp))
		elog(ERROR, "cache lookup failed for user mapping %u", fc_umId);

	memset(fc_repl_val, 0, sizeof(fc_repl_val));
	memset(fc_repl_null, false, sizeof(fc_repl_null));
	memset(fc_repl_repl, false, sizeof(fc_repl_repl));

	if (fc_stmt->options)
	{
		ForeignDataWrapper *fc_fdw;
		Datum		fc_datum;
		bool		fc_isnull;

		/*
		 * 处理选项。
		 */

		fc_fdw = GetForeignDataWrapper(fc_srv->fdwid);

		fc_datum = SysCacheGetAttr(USERMAPPINGUSERSERVER,
								fc_tp,
								Anum_pg_user_mapping_umoptions,
								&fc_isnull);
		if (fc_isnull)
			fc_datum = PointerGetDatum(NULL);

		/* 准备选项数组 */
		fc_datum = transformGenericOptions(UserMappingRelationId,
										fc_datum,
										fc_stmt->options,
										fc_fdw->fdwvalidator);

		if (PointerIsValid(DatumGetPointer(fc_datum)))
			fc_repl_val[Anum_pg_user_mapping_umoptions - 1] = fc_datum;
		else
			fc_repl_null[Anum_pg_user_mapping_umoptions - 1] = true;

		fc_repl_repl[Anum_pg_user_mapping_umoptions - 1] = true;
	}

	/* 一切看起来都很好 - 更新元组 */
	fc_tp = heap_modify_tuple(fc_tp, RelationGetDescr(fc_rel),
						   fc_repl_val, fc_repl_null, fc_repl_repl);

	CatalogTupleUpdate(fc_rel, &fc_tp->t_self, fc_tp);

	InvokeObjectPostAlterHook(UserMappingRelationId,
							  fc_umId, 0);

	ObjectAddressSet(fc_address, UserMappingRelationId, fc_umId);

	heap_freetuple(fc_tp);

	table_close(fc_rel, RowExclusiveLock);

	return fc_address;
}


/*
 * 删除用户映射
 */
Oid RemoveUserMapping(DropUserMappingStmt *fc_stmt)
{
	ObjectAddress fc_object;
	Oid			fc_useId;
	Oid			fc_umId;
	ForeignServer *fc_srv;
	RoleSpec   *fc_role = (RoleSpec *) fc_stmt->user;

	if (fc_role->roletype == ROLESPEC_PUBLIC)
		fc_useId = ACL_ID_PUBLIC;
	else
	{
		fc_useId = get_rolespec_oid(fc_stmt->user, fc_stmt->missing_ok);
		if (!OidIsValid(fc_useId))
		{
			/*
			 * 如果指定了 IF EXISTS，角色未找到且不公共。注意这一点
			 * 并离开。
			 */
			elog(NOTICE, "role \"%s\" does not exist, skipping",
				 fc_role->rolename);
			return InvalidOid;
		}
	}

	fc_srv = GetForeignServerByName(fc_stmt->servername, true);

	if (!fc_srv)
	{
		if (!fc_stmt->missing_ok)
			ereport(ERROR,
					(errcode(ERRCODE_UNDEFINED_OBJECT),
					 errmsg("server \"%s\" does not exist",
							fc_stmt->servername)));
		/* 如果存在，记下它 */
		ereport(NOTICE,
				(errmsg("server \"%s\" does not exist, skipping",
						fc_stmt->servername)));
		return InvalidOid;
	}

	fc_umId = GetSysCacheOid2(USERMAPPINGUSERSERVER, Anum_pg_user_mapping_oid,
						   ObjectIdGetDatum(fc_useId),
						   ObjectIdGetDatum(fc_srv->serverid));

	if (!OidIsValid(fc_umId))
	{
		if (!fc_stmt->missing_ok)
			ereport(ERROR,
					(errcode(ERRCODE_UNDEFINED_OBJECT),
					 errmsg("user mapping for \"%s\" does not exist for server \"%s\"",
							MappingUserName(fc_useId), fc_stmt->servername)));

		/* 如果指定了 IF EXISTS，仅记下它 */
		ereport(NOTICE,
				(errmsg("user mapping for \"%s\" does not exist for server \"%s\", skipping",
						MappingUserName(fc_useId), fc_stmt->servername)));
		return InvalidOid;
	}

	fc_user_mapping_ddl_aclcheck(fc_useId, fc_srv->serverid, fc_srv->servername);

	/*
	 * 执行删除
	 */
	fc_object.classId = UserMappingRelationId;
	fc_object.objectId = fc_umId;
	fc_object.objectSubId = 0;

	performDeletion(&fc_object, DROP_CASCADE, 0);

	return fc_umId;
}


/*
 * 创建外部表
 * 在 DefineRelation() 之后调用。
 */
void CreateForeignTable(CreateForeignTableStmt *fc_stmt, Oid fc_relid)
{
	Relation	fc_ftrel;
	Datum		fc_ftoptions;
	Datum		fc_values[Natts_pg_foreign_table];
	bool		fc_nulls[Natts_pg_foreign_table];
	HeapTuple	fc_tuple;
	AclResult	fc_aclresult;
	ObjectAddress fc_myself;
	ObjectAddress fc_referenced;
	Oid			fc_ownerId;
	ForeignDataWrapper *fc_fdw;
	ForeignServer *fc_server;

	/*
	 * 提前命令计数器以确保 pg_attribute 元组可见；
	 * 元组可能在之前的步骤中更新以添加约束。
	 */
	CommandCounterIncrement();

	fc_ftrel = table_open(ForeignTableRelationId, RowExclusiveLock);

	/*
	 * 目前无法指定所有者。使用有效用户 ID。
	 */
	fc_ownerId = GetUserId();

	/*
	 * 检查外部服务器是否存在，以及我们是否对其拥有 USAGE 权限。同时
	 * 获取实际的 FDW 以进行选项验证等。
	 */
	fc_server = GetForeignServerByName(fc_stmt->servername, false);
	fc_aclresult = pg_foreign_server_aclcheck(fc_server->serverid, fc_ownerId, ACL_USAGE);
	if (fc_aclresult != ACLCHECK_OK)
		aclcheck_error(fc_aclresult, OBJECT_FOREIGN_SERVER, fc_server->servername);

	fc_fdw = GetForeignDataWrapper(fc_server->fdwid);

	/*
	 * 在 pg_foreign_table 中插入元组。
	 */
	memset(fc_values, 0, sizeof(fc_values));
	memset(fc_nulls, false, sizeof(fc_nulls));

	fc_values[Anum_pg_foreign_table_ftrelid - 1] = ObjectIdGetDatum(fc_relid);
	fc_values[Anum_pg_foreign_table_ftserver - 1] = ObjectIdGetDatum(fc_server->serverid);
	/* 添加表的一般选项 */
	fc_ftoptions = transformGenericOptions(ForeignTableRelationId,
										PointerGetDatum(NULL),
										fc_stmt->options,
										fc_fdw->fdwvalidator);

	if (PointerIsValid(DatumGetPointer(fc_ftoptions)))
		fc_values[Anum_pg_foreign_table_ftoptions - 1] = fc_ftoptions;
	else
		fc_nulls[Anum_pg_foreign_table_ftoptions - 1] = true;

	fc_tuple = heap_form_tuple(fc_ftrel->rd_att, fc_values, fc_nulls);

	CatalogTupleInsert(fc_ftrel, fc_tuple);

	heap_freetuple(fc_tuple);

	/* 添加 pg_class 对服务器的依赖 */
	fc_myself.classId = RelationRelationId;
	fc_myself.objectId = fc_relid;
	fc_myself.objectSubId = 0;

	fc_referenced.classId = ForeignServerRelationId;
	fc_referenced.objectId = fc_server->serverid;
	fc_referenced.objectSubId = 0;
	recordDependencyOn(&fc_myself, &fc_referenced, DEPENDENCY_NORMAL);

	table_close(fc_ftrel, RowExclusiveLock);
}

/*
 * 导入外部模式
 */
void ImportForeignSchema(ImportForeignSchemaStmt *fc_stmt)
{
	ForeignServer *fc_server;
	ForeignDataWrapper *fc_fdw;
	FdwRoutine *fc_fdw_routine;
	AclResult	fc_aclresult;
	List	   *fc_cmd_list;
	ListCell   *fc_lc;

	/* 检查外部服务器是否存在，以及我们是否对其拥有 USAGE 权限 */
	fc_server = GetForeignServerByName(fc_stmt->server_name, false);
	fc_aclresult = pg_foreign_server_aclcheck(fc_server->serverid, GetUserId(), ACL_USAGE);
	if (fc_aclresult != ACLCHECK_OK)
		aclcheck_error(fc_aclresult, OBJECT_FOREIGN_SERVER, fc_server->servername);

	/* 检查模式是否存在，我们对其拥有 CREATE 权限 */
	(void) LookupCreationNamespace(fc_stmt->local_schema);

	/* 获取 FDW 并检查其是否支持 IMPORT */
	fc_fdw = GetForeignDataWrapper(fc_server->fdwid);
	if (!OidIsValid(fc_fdw->fdwhandler))
		ereport(ERROR,
				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
				 errmsg("foreign-data wrapper \"%s\" has no handler",
						fc_fdw->fdwname)));
	fc_fdw_routine = GetFdwRoutine(fc_fdw->fdwhandler);
	if (fc_fdw_routine->ImportForeignSchema == NULL)
		ereport(ERROR,
				(errcode(ERRCODE_FDW_NO_SCHEMAS),
				 errmsg("foreign-data wrapper \"%s\" does not support IMPORT FOREIGN SCHEMA",
						fc_fdw->fdwname)));

	/* 调用 FDW 以获取命令列表 */
	fc_cmd_list = fc_fdw_routine->ImportForeignSchema(fc_stmt, fc_server->serverid);

	/* 解析并执行每个命令 */
	foreach(fc_lc, fc_cmd_list)
	{
		char	   *fc_cmd = (char *) lfirst(fc_lc);
		import_error_callback_arg fc_callback_arg;
		ErrorContextCallback fc_sqlerrcontext;
		List	   *fc_raw_parsetree_list;
		ListCell   *fc_lc2;

		/*
		 * 为 ereport() 设置错误回溯支持。这是为了
		 * 让生成的 SQL 中的任何错误都能妥善显示。
		 */
		fc_callback_arg.tablename = NULL;	/* 尚不清楚 */
		fc_callback_arg.cmd = fc_cmd;
		fc_sqlerrcontext.callback = fc_import_error_callback;
		fc_sqlerrcontext.arg = (void *) &fc_callback_arg;
		fc_sqlerrcontext.previous = error_context_stack;
		error_context_stack = &fc_sqlerrcontext;

		/*
		 * 将 SQL 字符串解析为原始解析树列表。
		 */
		fc_raw_parsetree_list = pg_parse_query(fc_cmd);

		/*
		 * 处理每个解析树（我们允许 FDW 在每个字符串中放置多个
		 * 命令，尽管这实际上并不建议）。
		 */
		foreach(fc_lc2, fc_raw_parsetree_list)
		{
			RawStmt    *fc_rs = lfirst_node(RawStmt, fc_lc2);
			CreateForeignTableStmt *fc_cstmt = (CreateForeignTableStmt *) fc_rs->stmt;
			PlannedStmt *fc_pstmt;

			/*
			 * 因为我们只允许 CreateForeignTableStmt，所以我们可以跳过解析
			 * 分析、重写和计划步骤。
			 */
			if (!IsA(fc_cstmt, CreateForeignTableStmt))
				elog(ERROR,
					 "foreign-data wrapper \"%s\" returned incorrect statement type %d",
					 fc_fdw->fdwname, (int) nodeTag(fc_cstmt));

			/* 忽略被过滤选项排除的表的命令 */
			if (!IsImportableForeignTable(fc_cstmt->base.relation->relname, fc_stmt))
				continue;

			/* 确保在出错时报告当前表的名称 */
			fc_callback_arg.tablename = fc_cstmt->base.relation->relname;

			/* 确保创建模式是 IMPORT 语句中给出的模式 */
			fc_cstmt->base.relation->schemaname = pstrdup(fc_stmt->local_schema);

			/* 不需要计划，只需创建一个包装的 PlannedStmt */
			fc_pstmt = makeNode(PlannedStmt);
			fc_pstmt->commandType = CMD_UTILITY;
			fc_pstmt->canSetTag = false;
			fc_pstmt->utilityStmt = (Node *) fc_cstmt;
			fc_pstmt->stmt_location = fc_rs->stmt_location;
			fc_pstmt->stmt_len = fc_rs->stmt_len;

			/* 执行语句 */
			ProcessUtility(fc_pstmt, fc_cmd, false,
						   PROCESS_UTILITY_SUBCOMMAND, NULL, NULL,
						   None_Receiver, NULL);

			/* 确保在子命令之间推进命令计数器 */
			CommandCounterIncrement();

			fc_callback_arg.tablename = NULL;
		}

		error_context_stack = fc_sqlerrcontext.previous;
	}
}

/*
 * 错误上下文回调，以便让我们提供失败的 SQL 语句文本
 */
static void fc_import_error_callback(void *fc_arg)
{
	import_error_callback_arg *fc_callback_arg = (import_error_callback_arg *) fc_arg;
	int			fc_syntaxerrposition;

	/* 如果是语法错误，则转换为内部语法错误报告 */
	fc_syntaxerrposition = geterrposition();
	if (fc_syntaxerrposition > 0)
	{
		errposition(0);
		internalerrposition(fc_syntaxerrposition);
		internalerrquery(fc_callback_arg->cmd);
	}

	if (fc_callback_arg->tablename)
		errcontext("importing foreign table \"%s\"",
				   fc_callback_arg->tablename);
}
